package net.ericaro.neojavagengen;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import net.ericaro.neojavagen.Literals;
import net.ericaro.neojavagen.expressions.AdditiveExpression;
import net.ericaro.neojavagen.expressions.AndExpression;
import net.ericaro.neojavagen.expressions.ConditionalAndExpression;
import net.ericaro.neojavagen.expressions.ConditionalOrExpression;
import net.ericaro.neojavagen.expressions.EqualityExpression;
import net.ericaro.neojavagen.expressions.ExclusiveOrExpression;
import net.ericaro.neojavagen.expressions.InclusiveOrExpression;
import net.ericaro.neojavagen.expressions.MultiplicativeExpression;
import net.ericaro.neojavagen.expressions.RelationalExpression;
import net.ericaro.neojavagen.expressions.ShiftExpression;
import net.ericaro.neojavagen.expressions.UnaryExpression;
import net.ericaro.neojavagen.types.ActualTypeArgument;
import net.ericaro.neojavagen.types.ClassOrInterfaceType;
import net.ericaro.neojavagen.types.Identifier;
import net.ericaro.neojavagen.types.TypeArguments;
import net.ericaro.neojavagen.types.TypedIdentifier;

public class Grammar {
	
	String currentPackage;

	MetaInterface createInterface(String name){
		MetaInterface i = new MetaInterface(this,  name);
		return i;
	}
	
	MetaEnum createEnum(String name) {
		MetaEnum metaEnum = new MetaEnum(this,  name);
		return metaEnum;
	}
	MetaComposition createComposition(String name) {
		MetaComposition metaComposition = new MetaComposition(this,  name);
		return metaComposition;
	}
	MetaList createList(String name, String divider, String startSymbol, String endSymbol, String referencedType) {
		MetaList metaList = new MetaList(this,  name, divider, startSymbol, endSymbol, referencedType);
		return metaList;
	}
	
	
	void createPackage(String name) { 
		this.currentPackage = name;
	}

	Map<String, MetaType> types = new HashMap<String, MetaType>();
	
	public MetaType createMetaType(String name) {
		if (types.containsKey(name)) return types.get(name);
		else{
			MetaType type = new MetaType(this, name);
			types.put(name, type);
			return type;
		}
	}
	
	
	
	public static File fileFor(File sourcepath, MetaType m){
		File p = sourcepath;
		for (Identifier  i: m.getQualifiedPackageName() )
			p = new File( p, i.toString() );
		
		// p is the package
		p.mkdirs();
		return new File(p, m.getNameIdentifier().toString()+".java");
		
	}
	
	public static void dumpToFile(File sourcepath, MetaType m){
		File f = fileFor(sourcepath, m);
		try {
			FileWriter out = new FileWriter(f);
			out.write(m.implementation.getCompilationUnit().toString() );
			out.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	
	
	public static final Set<String> RESERVED_WORD;
	static{
		
	HashSet<String> set = new HashSet<String>();
	for(String v : new String[]{  "abstract",	"continue",	"for",		   "new",		  "switch",
	                            				"assert",	  "default",	 "if",			"package",	  "synchronized",
	                            				"boolean",	 "do",		  "goto",		  "private",	  "this",
	                            				"break",	   "double",	  "implements",	"protected",	"throw",
	                            				"byte",		"else",		"import",		"public",	   "throws",
	                            				"case",		"enum",		"instanceof",	"return",	   "transient",
	                            				"catch",	   "extends",	 "int",		   "short",		"try",
	                            				"char",		"final",	   "interface",	 "static",	   "void", 
	                            				"class",	   "finally",	 "long",		  "strictfp",	 "volatile",
	                            				"const",	   "float",	   "native",		"super",		"while"})
		set.add(v);
	
	RESERVED_WORD = Collections.unmodifiableSet(set);
	}
	
	
	public static final Map<String, String> DIACRITICS ;
	static{
		HashMap<String, String> map = new HashMap<String, String>();
		
		map.put(","    , "comma"          );
		map.put("..."  , "tripleDot"      );
		map.put("="    , "equalsSymbol"   );
		map.put("*="   , "starEquals"     );
		map.put("/="   , "slashEquals"    );
		map.put("%="   , "percentEquals"  );
		map.put("+="   , "plusEquals"     );
		map.put("-="   , "minusEquals"    );
		map.put("<<="  , "llEquals"       );
		map.put(">>="  , "ssEquals"       );
		map.put(">>>=" , "sssEquals"      );
		map.put("&="   , "andEquals"      );
		map.put("^="   , "xorEquals"      );
		map.put("|="   , "orEquals"       );
		DIACRITICS = Collections.unmodifiableMap(map);
	}
	
	
	public static Identifier nameSymbol(String value){
		
		if ( RESERVED_WORD.contains(value) ) 
				return Literals.identifier("a"+value);
		
		if (DIACRITICS.containsKey(value))
			return Literals.identifier(DIACRITICS.get(value));
		
		return Literals.identifier(value);
	}
	
	public static ClassOrInterfaceType fromJava( Class<?> type, ActualTypeArgument ... actualTypeArguments){
		ClassOrInterfaceType typedIdentifiers = new ClassOrInterfaceType();
		String[] names = type.getName().split("\\.");
		int i=0;
		for(;i <names.length-1;i++){
			String name=  names[i]; 
			typedIdentifiers.add(new TypedIdentifier(Literals.identifier(name)));
		}
		
		TypeArguments typeArguments = null;
		if (actualTypeArguments.length !=0 )
			typeArguments = new TypeArguments(actualTypeArguments);
		
		TypedIdentifier last = new TypedIdentifier(Literals.identifier(names[i]), typeArguments);
		typedIdentifiers.add(last);
		return typedIdentifiers;
	}
	
	
	public static ConditionalOrExpression unaryExpression(UnaryExpression unary){
		return new ConditionalOrExpression(
				new ConditionalAndExpression(
						new InclusiveOrExpression(
								new ExclusiveOrExpression(
										new AndExpression(
												new EqualityExpression(
														new RelationalExpression(
																new ShiftExpression(
																		new AdditiveExpression(
																				new MultiplicativeExpression(unary)
																		)
																		
																)
																
														)
													)
									)
								)
						)
				)
		);
	}
	
	public static void main(String[] args) {
		if (args.length!=1) return;
		File sourcepath = new File(args[0]);
		
		Grammar g = JavaGrammar.parse(new Grammar() );
		for(MetaType m  : g.types.values()){
			System.out.println("dumping "+ m.getClassOrInterfaceType());
			dumpToFile(sourcepath, m);
		}
		System.out.println(g.types.size());
		
	}
	
	
}
